"""
遗传算法求解函数最大值
函数：f(x) = x * cos(x) + 2
作者：徐有才
学号：202316034203
日期：2025/11/6
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import random

# 设置中文字体显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

class GeneticAlgorithm:
    def __init__(self, population_size=50, chromosome_length=20, crossover_rate=0.8, 
                 mutation_rate=0.01, generations=100, x_range=(-10, 10)):
        """
        初始化遗传算法参数
        
        Args:
            population_size: 种群大小
            chromosome_length: 染色体长度（二进制编码）
            crossover_rate: 交叉概率
            mutation_rate: 变异概率
            generations: 迭代代数
            x_range: x的取值范围
        """
        self.population_size = population_size
        self.chromosome_length = chromosome_length
        self.crossover_rate = crossover_rate
        self.mutation_rate = mutation_rate
        self.generations = generations
        self.x_range = x_range
        
        # 初始化种群
        self.population = self.initialize_population()
        self.best_individual = None
        self.best_fitness = -float('inf')
        self.fitness_history = []
        
    def initialize_population(self):
        """初始化种群"""
        population = []
        for _ in range(self.population_size):
            # 随机生成二进制染色体
            chromosome = [random.randint(0, 1) for _ in range(self.chromosome_length)]
            population.append(chromosome)
        return population
    
    def decode_chromosome(self, chromosome):
        """将二进制染色体解码为实数x"""
        # 将二进制转换为十进制
        decimal_value = 0
        for i, gene in enumerate(chromosome):
            decimal_value += gene * (2 ** (self.chromosome_length - 1 - i))
        
        # 映射到x的取值范围
        min_x, max_x = self.x_range
        x = min_x + decimal_value * (max_x - min_x) / (2 ** self.chromosome_length - 1)
        return x
    
    def fitness_function(self, x):
        """适应度函数：f(x) = x * cos(x) + 2"""
        return x * np.cos(x) + 2
    
    def evaluate_fitness(self):
        """评估种群中每个个体的适应度"""
        fitness_values = []
        for chromosome in self.population:
            x = self.decode_chromosome(chromosome)
            fitness = self.fitness_function(x)
            fitness_values.append(fitness)
        return fitness_values
    
    def selection(self, fitness_values):
        """选择操作：轮盘赌选择"""
        # 处理负的适应度值
        min_fitness = min(fitness_values)
        if min_fitness < 0:
            fitness_values = [f - min_fitness + 1 for f in fitness_values]
        
        total_fitness = sum(fitness_values)
        probabilities = [f / total_fitness for f in fitness_values]
        
        # 轮盘赌选择
        selected_indices = []
        for _ in range(self.population_size):
            r = random.random()
            cumulative_prob = 0
            for i, prob in enumerate(probabilities):
                cumulative_prob += prob
                if cumulative_prob >= r:
                    selected_indices.append(i)
                    break
        
        new_population = [self.population[i] for i in selected_indices]
        return new_population
    
    def crossover(self, parent1, parent2):
        """交叉操作：单点交叉"""
        if random.random() < self.crossover_rate:
            # 随机选择交叉点
            crossover_point = random.randint(1, self.chromosome_length - 1)
            child1 = parent1[:crossover_point] + parent2[crossover_point:]
            child2 = parent2[:crossover_point] + parent1[crossover_point:]
            return child1, child2
        else:
            return parent1, parent2
    
    def mutation(self, chromosome):
        """变异操作：位翻转"""
        mutated_chromosome = chromosome.copy()
        for i in range(self.chromosome_length):
            if random.random() < self.mutation_rate:
                mutated_chromosome[i] = 1 - mutated_chromosome[i]  # 翻转位
        return mutated_chromosome
    
    def run(self):
        """运行遗传算法"""
        print("开始遗传算法优化...")
        print(f"目标函数: f(x) = x * cos(x) + 2")
        print(f"x取值范围: [{self.x_range[0]}, {self.x_range[1]}]")
        print("-" * 50)
        
        for generation in range(self.generations):
            # 评估适应度
            fitness_values = self.evaluate_fitness()
            
            # 记录最佳个体
            current_best_fitness = max(fitness_values)
            current_best_index = fitness_values.index(current_best_fitness)
            
            if current_best_fitness > self.best_fitness:
                self.best_fitness = current_best_fitness
                self.best_individual = self.population[current_best_index]
            
            self.fitness_history.append(current_best_fitness)
            
            # 每10代输出一次进度
            if generation % 10 == 0:
                best_x = self.decode_chromosome(self.best_individual)
                print(f"第{generation:3d}代: 最佳适应度 = {self.best_fitness:.6f}, 最佳x = {best_x:.6f}")
            
            # 选择
            selected_population = self.selection(fitness_values)
            
            # 交叉
            new_population = []
            for i in range(0, self.population_size, 2):
                parent1 = selected_population[i]
                parent2 = selected_population[(i + 1) % self.population_size]
                child1, child2 = self.crossover(parent1, parent2)
                new_population.extend([child1, child2])
            
            # 变异
            self.population = [self.mutation(chromosome) for chromosome in new_population]
        
        # 最终结果
        best_x = self.decode_chromosome(self.best_individual)
        print("-" * 50)
        print("优化完成!")
        print(f"最优解: x = {best_x:.6f}")
        print(f"最大值: f(x) = {self.best_fitness:.6f}")
        
        return best_x, self.best_fitness
    
    def plot_results(self):
        """绘制结果并分别保存"""
        best_x = self.decode_chromosome(self.best_individual)
        best_y = self.best_fitness
        
        # 图1: 函数图像和最优解
        plt.figure(figsize=(10, 6))
        x_vals = np.linspace(self.x_range[0], self.x_range[1], 1000)
        y_vals = self.fitness_function(x_vals)
        
        plt.plot(x_vals, y_vals, 'b-', label='f(x) = x*cos(x) + 2', linewidth=2)
        plt.plot(best_x, best_y, 'ro', markersize=8, label=f'最优解: x={best_x:.3f}')
        plt.xlabel('x')
        plt.ylabel('f(x)')
        plt.title('函数图像和最优解')
        plt.legend()
        plt.grid(True, alpha=0.3)
        plt.tight_layout()
        save_path = 'D:\\Main\\大学课程\\人工智能\\人工智能作业代码\\5-7遗传算法求函数最大值\\'
        plt.savefig(save_path + '函数图像和最优解.png', dpi=300, bbox_inches='tight')
        plt.show()  # 保留弹窗显示
        
        # 图2: 适应度进化曲线
        plt.figure(figsize=(10, 6))
        plt.plot(self.fitness_history, 'g-', linewidth=2)
        plt.xlabel('迭代代数')
        plt.ylabel('最佳适应度')
        plt.title('适应度进化曲线')
        plt.grid(True, alpha=0.3)
        plt.tight_layout()
        plt.savefig(save_path + '适应度进化曲线.png', dpi=300, bbox_inches='tight')
        plt.show()  # 保留弹窗显示
        
        # 图3: 3D函数曲面
        plt.figure(figsize=(10, 6))
        ax = plt.axes(projection='3d')
        X = np.linspace(self.x_range[0], self.x_range[1], 100)
        Y = np.linspace(-3, 3, 100)
        X, Y = np.meshgrid(X, Y)
        Z = X * np.cos(X) + 2
        
        surf = ax.plot_surface(X, Y, Z, cmap=cm.viridis, alpha=0.8)
        ax.scatter(best_x, 0, best_y, color='red', s=100, label='最优解')
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title('3D函数曲面')
        ax.legend()
        
        plt.tight_layout()
        plt.savefig(save_path + '3D函数曲面.png', dpi=300, bbox_inches='tight')
        plt.show()  # 保留弹窗显示
        
        print("图表已分别保存为：")
        print("- 函数图像和最优解.png")
        print("- 适应度进化曲线.png") 
        print("- 3D函数曲面.png")

def main():
    """主函数"""
    # 设置遗传算法参数
    ga = GeneticAlgorithm(
        population_size=50,
        chromosome_length=20,
        crossover_rate=0.8,
        mutation_rate=0.01,
        generations=100,
        x_range=(-5, 20)
    )
    
    # 运行遗传算法
    best_x, best_fitness = ga.run()
    
    # 绘制结果
    ga.plot_results()
    
    # 验证结果
    print("\n验证结果:")
    print(f"f({best_x:.6f}) = {best_fitness:.6f}")
    
    # 在最优解附近进行局部搜索验证
    print("\n局部验证:")
    for dx in [-0.1, -0.05, 0, 0.05, 0.1]:
        x_test = best_x + dx
        f_test = ga.fitness_function(x_test)
        print(f"f({x_test:.6f}) = {f_test:.6f}")

if __name__ == "__main__":
    main()